#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "wifi_starter.h"
#include "wifi_connecter.h"
#include "lwip/sockets.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"
#include "iot_errno.h"
#include "iot_gpio.h"
#include "iot_gpio_ex.h"

#define IS_MASTER 1 // 是否为主节点（主节点为 AP, 子节点 STA 连接至主节点）
#define NODE_ID 0 // 节点编号（奇数亮的灯相同, 偶数亮的灯相反）

void SetLight(int type)
{
    IoTGpioSetOutputVal(13, IOT_GPIO_VALUE0);
    IoTGpioSetOutputVal(12, IOT_GPIO_VALUE0);
    IoTGpioSetOutputVal(9, IOT_GPIO_VALUE0);
    switch (type)
    {
        default:
        case 0: IoTGpioSetOutputVal(13, IOT_GPIO_VALUE1); break;
        case 1:
            IoTGpioSetOutputVal(13, IOT_GPIO_VALUE1);
            IoTGpioSetOutputVal(12, IOT_GPIO_VALUE1);
            break;
        case 2: IoTGpioSetOutputVal(12, IOT_GPIO_VALUE1); break;
    }
}

static void MeshTask(void)
{
    // 初始化 GPIO13
    IoTGpioInit(13);
    IoTGpioSetFunc(13, IOT_GPIO_FUNC_GPIO_13_GPIO);
    IoTGpioSetDir(13, IOT_GPIO_DIR_OUT);
    // 初始化 GPIO12
    IoTGpioInit(12);
    IoTGpioSetFunc(12, IOT_GPIO_FUNC_GPIO_12_GPIO);
    IoTGpioSetDir(12, IOT_GPIO_DIR_OUT);
    // 初始化 GPIO9
    IoTGpioInit(9);
    IoTGpioSetFunc(9, IOT_GPIO_FUNC_GPIO_9_GPIO);
    IoTGpioSetDir(9, IOT_GPIO_DIR_OUT);

#if IS_MASTER != 0
    // 开启热点
    HotspotConfig config = {0};
    strcpy_s(config.ssid, sizeof(config.ssid), "Hi3861");
    strcpy_s(config.preSharedKey, sizeof(config.preSharedKey), "1234567890");
    config.securityType = WIFI_SEC_TYPE_PSK;
    config.band = HOTSPOT_BAND_TYPE_2G;
    config.channelNum = 7;
    printf("Starting hotspot...\n");
    WifiErrorCode errCode = StartHotspot(&config);
    printf("StartHotspot: %d\n", errCode);
    sleep(5);
    // 创建 Socket
    int fd = socket(PF_INET, SOCK_DGRAM, 0);
    if (fd < 0)
    {
        printf("Socket allocate failed, errno=%d\n", errno);
        return;
    }
    int opt = 1;
    // 设置该套接字为广播类型
    if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*) &opt, sizeof(opt)) < 0)
    {
        printf("Set socket broadcast failed\n");
        lwip_close(fd);
        return;
    }
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
    addr.sin_port = htons(3861);
    while (1)
    {
        char buffer;
        // 变为红灯
        SetLight(0);
        // 发送信号灯状态
        buffer = 'R';
        if (sendto(fd, &buffer, sizeof(buffer), 0, (struct sockaddr*) &addr, sizeof(addr)) < 0)
        {
            printf("Send broadcast failed!\n");
        }
        // 等待 30 秒
        sleep(30);
        // 变为黄灯
        SetLight(1);
        buffer = 'Y';
        if (sendto(fd, &buffer, sizeof(buffer), 0, (struct sockaddr*) &addr, sizeof(addr)) < 0)
        {
            printf("Send broadcast failed!\n");
        }
        sleep(5);
        // 变为绿灯
        SetLight(2);
        buffer = 'G';
        if (sendto(fd, &buffer, sizeof(buffer), 0, (struct sockaddr*) &addr, sizeof(addr)) < 0)
        {
            printf("Send broadcast failed!\n");
        }
        sleep(30);
        // 变为黄灯
        SetLight(1);
        buffer = 'Y';
        if (sendto(fd, &buffer, sizeof(buffer), 0, (struct sockaddr*) &addr, sizeof(addr)) < 0)
        {
            printf("Send broadcast failed!\n");
        }
        sleep(5);
    }
#else
    // 连接至热点
    WifiDeviceConfig config = {0};
    strcpy_s(config.ssid, sizeof(config.ssid), "Hi3861");
    strcpy_s(config.preSharedKey, sizeof(config.preSharedKey), "1234567890");
    config.securityType = WIFI_SEC_TYPE_PSK;
    printf("Connecting wifi...\n");
    int netId = ConnectToHotspot(&config);
    printf("ConnectToHotspot: %d\n", netId);
    sleep(5);
    // 填充 sockaddr_in 结构体
    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(3861);
    // 创建 Socket
    int fd = socket(PF_INET, SOCK_DGRAM, 0);
    if (fd < 0)
    {
        printf("Socket allocate failed, errno=%d\n", errno);
        return;
    }
    int opt = 1;
    // 设置该套接字为广播类型
    if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*) &opt, sizeof(opt)) < 0)
    {
        printf("Set socket broadcast failed\n");
        lwip_close(fd);
        return;
    }
    // 开始监听
    if (bind(fd, (struct sockaddr*) &server_addr, sizeof(server_addr)) != 0)
    {
        printf("Socket bind failed\n");
        lwip_close(fd);
        return;
    }
    printf("Socket listening\n");
    // 广播地址
    struct sockaddr_in addr;
    bzero(&addr, sizeof(struct sockaddr_in));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(3861);
    int addr_len = sizeof(addr);
    while (1)
    {
        char message;
        int ret = recvfrom(fd, &message, sizeof(message), 0, (struct sockaddr*) &addr, (socklen_t*) &addr_len);
        if (ret < 0)
        {
            printf("recvfrom failed, ret=%d!\n", ret);
            continue;
        }
        switch (message)
        {
            default:
            case 'R': SetLight(NODE_ID % 2 == 0 ? 0 : 2); break;
            case 'Y': SetLight(1); break;
            case 'G': SetLight(NODE_ID % 2 == 0 ? 2 : 0); break;
        }
        usleep(1000);
    }
#endif
}

static void MeshDemo(void)
{
    osThreadAttr_t attr;
    attr.name = "MeshTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 8192;
    attr.priority = osPriorityNormal;
    if (osThreadNew(MeshTask, NULL, &attr) == NULL)
    {
        printf("Failed to create MeshTask!\n");
    }
}

SYS_RUN(MeshDemo);
